package com.minigame.card.mahjong.domain;

import com.minigame.card.domain.CardPlayer;
import com.minigame.card.enums.Direction;
import com.minigame.card.mahjong.enums.HuRule;
import com.minigame.card.mahjong.enums.MahjongCard;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.*;

import static cn.hutool.core.collection.CollUtil.countMap;
import static cn.hutool.core.collection.IterUtil.isNotEmpty;
import static com.minigame.card.mahjong.enums.HuRule.Z;
import static com.minigame.card.mahjong.util.MahjongUtil.*;
import static com.minigame.card.util.CardUtil.countList;
import static com.minigame.card.util.CardUtil.countPlayerCell;
import static java.util.Collections.emptyList;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;

@Data
@EqualsAndHashCode(callSuper = false)
public class MahjongCardPlayer extends CardPlayer<MahjongCard> {
    private static final int CELL_COUNT = MahjongCard.values().length;
    private static final int TYPE_COUNT = MahjongCard.WAN_INDEXES.size();

    /**
     * 尝试吃
     * @param discard 其他玩家丢弃牌
     * @return 吃后形成的固定牌型列表
     */
    public List<List<MahjongCard>> tryCh(MahjongCard discard) {
        int[] counts = countList(up, CELL_COUNT, MahjongCard::getIndex);
        counts[discard.getIndex()]++;
        int[][] selections = tryMahjongCh(counts, TYPE_COUNT, discard.getIndex());
        if (canMahjongCh(selections)) {
            List<List<MahjongCard>> solids = new ArrayList<>();
            for (int[] selection : selections) {
                List<MahjongCard> solid = new ArrayList<>();
                solids.add(solid);
                for (int index : selection) {
                    solid.add(MahjongCard.ofIndex(index));
                }
            }
            return solids;
        }
        return emptyList();
    }

    /**
     * 能否吃
     * @param solids 吃后固定牌型列表
     * @param discard 其他玩家弃牌
     * @return 是否能吃
     */
    public boolean canCh(List<List<MahjongCard>> solids, MahjongCard discard) {
        return isNotEmpty(solids) || isNotEmpty(tryCh(discard));
    }

    @Override
    public boolean requiredSelect(List<List<MahjongCard>> options, MahjongCard discard) {
        return options.size() > 1;
    }

    /**
     * 尝试摸牌后补杠
     * @return 补杠牌列表
     */
    public List<MahjongCard> tryGang() {
        List<MahjongCard> downGangs = new ArrayList<>();
        Map<MahjongCard, Integer> upCountMap = countMap(up);
        for (List<MahjongCard> solid : down) {
            Map<MahjongCard, Integer> solidCountMap = countMap(solid);
            for (MahjongCard card : solidCountMap.keySet()) {
                if (upCountMap.get(card) != null && upCountMap.get(card) == 1 && solidCountMap.get(card) != null && solidCountMap.get(card) == 3) {
                    downGangs.add(card);
                }
            }
        }
        return downGangs;
    }

    /**
     * 返回满足规则的列表
     * @param gang 是否杠后摸牌
     * @return 所有胡牌枚举
     */
    public List<HuRule> huList(boolean gang) {
        List<HuRule> huList = new ArrayList<>();
        for (HuRule value : HuRule.values()) {
            if (value.rule(up, down, gang)) {
                if (value != Z || huList.isEmpty()) {
                    huList.add(value);
                }
            }
        }
        return huList;
    }

    /**
     * 返回满足规则的列表
     * @param discard 其他玩家弃牌
     * @param gang 是否杠后摸牌
     * @return 所有胡牌枚举
     */
    public List<HuRule> huList(MahjongCard discard, boolean gang) {
        List<HuRule> huList = new ArrayList<>();
        up.add(discard);
        for (HuRule value : HuRule.values()) {
            if (value.rule(up, down, gang)) {
                if (value != Z || huList.isEmpty()) {
                    huList.add(value);
                }
            }
        }
        up.remove(discard);
        up.sort(comparing(MahjongCard::getIndex));
        return huList;
    }

    @Override
    public Direction anticlockwiseNext(Direction rest) {
        return direction.anticlockwiseNext();
    }

    /**
     * 听牌
     * @return 打的牌和胡牌映射
     */
    public Map<MahjongCard, List<MahjongCard>> tryTing() {
        int[] counts = countPlayerCell(up, MahjongCard.values().length, MahjongCard::getIndex);
        int[][] downTeams = new int[down.size()][];
        for (int i = 0; i < down.size(); i++) {
            downTeams[i] = new int[down.get(i).size()];
            for (int j = 0; j < down.get(i).size(); j++) {
                downTeams[i][j] = down.get(i).get(j).getIndex();
            }
        }
        Map<MahjongCard, List<MahjongCard>> discardHuMap = new LinkedHashMap<>();
        for (Map.Entry<Integer, int[]> entry : tingMap(counts, downTeams).entrySet()) {
            discardHuMap.put(MahjongCard.ofIndex(entry.getKey()), Arrays.stream(entry.getValue()).mapToObj(MahjongCard::ofIndex).collect(toList()));
        }
        return discardHuMap;
    }
}
